#include <stdlib.h>
#include <stdio.h>
#include <fftw3.h>
#include <math.h>
#include <string.h>
#include <limits.h>

#include "bassbeat.h"
#include "light_control.h"

#define MAG(X) sqrt((long double)((X)[0] * (X)[0] + (X)[1] * (X)[1]))

sounddata * sounddata_init(unsigned int SAMPLESIZE, int rate)
{
        sounddata * p = (sounddata *)malloc(sizeof(sounddata));
        fftw_plan plan;
        double * fWave;
        fftw_complex *fFreq;

        p->l = light_init();

        p->rate = rate;
        p->SAMPLESIZE = SAMPLESIZE;
        p->nFrames = 0;

        memset(p->imm, 0, sizeof(float) * 3);
        memset(p->imm_rel, 0, sizeof(float) * 3);
        memset(p->avg, 0, sizeof(float) * 3);
        memset(p->avg_rel, 0, sizeof(float) * 3);
        memset(p->long_avg, 0, sizeof(float) * 3);

        fWave = (double *) fftw_malloc(sizeof(double) * SAMPLESIZE);
        fFreq = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * SAMPLESIZE);

        plan = fftw_plan_dft_r2c_1d(SAMPLESIZE, fWave, fFreq, FFTW_MEASURE);
        p->fftwplan = plan;
        p->fWave = fWave;
        p->fFreq = fFreq;

        return p;
}

void sounddata_destroy(sounddata * s) {
        free(s->fFreq);
        free(s->fWave);
        free(s);
}

sounddata * handle_sample(sounddata * s, int16_t * input_data)
{
        int i;

        for (i = 0; i < s->SAMPLESIZE; i++)
                s->fWave[i] = (double)input_data[i] / ((double)(SHRT_MAX + 1));

        memset(s->fFreq, 0, sizeof(fftw_complex) * s->SAMPLESIZE);

        fftw_execute(s->fftwplan);

        for (i = 0; i < s->SAMPLESIZE; i++) {
                s->fFreq[i][0] = MAG(s->fFreq[i]);
        }

        for (i = 0; i < 3; i++) {
                int start = s->SAMPLESIZE * i / 6;
                int end = s->SAMPLESIZE * (i + 1) / 6;
                int j;

                s->imm[i] = 0;

                for (j = start; j < end; j++) {
                        s->imm[i] += s->fFreq[j][0];
                }

                if (s->imm[i] > s->avg[i])
                        s->avg[i] = s->avg[i] * 0.2 + s->imm[i] * 0.8;
                else
                        s->avg[i] = s->avg[i] * 0.5 + s->imm[i] * 0.5;

                if (s->nFrames < 50)
                        s->long_avg[i] = s->long_avg[i] * 0.9 + s->imm[i]*0.1;
                else
                        s->long_avg[i] = s->long_avg[i] * 0.992 + s->imm[i]*0.008;

                if (fabsf(s->long_avg[i]) < 0.001)
                        s->imm_rel[i] = 1.0;
                else
                        s->imm_rel[i] = s->imm[i] / s->long_avg[i];

                if (fabsf(s->long_avg[i]) < 0.001)
                        s->avg_rel[i] = 1.0;
                else
                        s->avg_rel[i] = s->avg[i] / s->long_avg[i];
        }

        /*printf("Total volume: %0.2f, %0.2f\n", total_volume, s->imm_rel[0]); */

        if (s->imm_rel[0] > 1) {
                lighton(s->l, 0);
        }
        else {
                lightoff(s->l, 0);
        }

        s->nFrames ++;

        return s;
}
